<template>
  <div
    id="editor"
    class="editor"
    :style="{
      ...getCanvasStyle(canvasStyleData),
      width: changeStyleWithScale(canvasStyleData.width) + 'px',
      height: changeStyleWithScale(canvasStyleData.height) + 'px'
    }"
    @contextmenu="handleContextMenu"
    @mousedown="handleMouseDown"
  >
    <!-- 网格线 -->
    <Grid />
    <!--页面组件列表展示-->
    <Shape
      v-for="(item, index) in componentData"
      v-show="item.isShow !== false"
      :key="item.id"
      :active="item.id === (curComponent || {}).id"
      :element="item"
      :index="index"
      :class="{ lock: item.isLock }"
    >
      <component
        :is="item.component"
        v-if="item.component.startsWith('SVG')"
        :id="'component' + item.id"
        :style="getSVGStyle(item.style, svgFilterAttrs)"
        class="component"
        :prop-value="item.propValue"
        :element="item"
        :request="item.request"
        @editorEvent="editorEvent"
      />
      <component
        :is="item.component"
        v-else
        :id="'component' + item.id"
        class="component"
        :propValue="item.propValue"
        :element="item"
        :style="getComponentStyle(item.style)"
        @editorEvent="editorEvent"
      />
    </Shape>
    <!-- 标线 -->
    <MarkLine />
    <!-- 右击菜单 -->
    <ContextMenu />

    <!-- 选中区域 -->
    <Area v-show="isShowArea" :start="start" :width="width" :height="height" />
  </div>
</template>
<script setup lang="ts">
import { useComponent, useContextMenu, useCompose } from "@visual/stores/index";
import Shape from "./Shape.vue";
import MarkLine from "./MarkLine.vue";
import Area from "./Area.vue";
import ContextMenu from "../ContextMenu.vue";
import Grid from "./Grid.vue";
import { storeToRefs } from "pinia";
import { getStyle, getSVGStyle, getCanvasStyle, changeStyleWithScale, isPreventDrop, getComponentRotatedStyle, $, eventBus } from "@visual/utils";
import { onMounted, onUnmounted, ref } from "vue";
// import { DataAnalysis } from"@px-ui/icons-vue";

// Shape相关
const componentStore = useComponent();
const { componentData, curComponent, canvasStyleData } = storeToRefs(componentStore);
const { showContextMenu } = useContextMenu();
const { setAreaData } = useCompose();
let isCtrlOrCommandDown = false;
function handleContextMenu(e) {
  e.stopPropagation();
  e.preventDefault();

  // 计算菜单相对于编辑器的位移
  let target = e.target;
  let top = e.offsetY;
  let left = e.offsetX;
  while (target instanceof SVGElement) {
    target = target.parentNode;
  }

  while (!target.className.includes("editor")) {
    left += target.offsetLeft;
    top += target.offsetTop;
    target = target.parentNode;
  }

  showContextMenu({ top, left });
}

// 普通Component相关
const svgFilterAttrs = ["width", "height", "top", "left", "rotate"];
function getComponentStyle(style) {
  return getStyle(style, svgFilterAttrs);
}

const $editor = ref(null);
const isShowArea = ref(false);
const width = ref(0);
const height = ref(0);
const start = ref({ x: 0, y: 0 });
const editorX = ref(0);
const editorY = ref(0);
function handleMouseDown(e) {
  // 如果没有选中组件 在画布上点击时需要调用 e.preventDefault() 防止触发 drop 事件
  // if (e.currentTarget !== e.target) {
  //   return;
  // }
  if (!curComponent.value || isPreventDrop(curComponent.value.component)) {
    e.preventDefault();
  }

  hideArea();

  // 获取编辑器的位移信息，每次点击时都需要获取一次。主要是为了方便开发时调试用。
  const rectInfo = $editor.value.getBoundingClientRect();
  editorX.value = rectInfo.x;
  editorY.value = rectInfo.y;

  const startX = e.clientX;
  const startY = e.clientY;
  start.value.x = startX - editorX.value;
  start.value.y = startY - editorY.value;
  // 展示选中区域
  isShowArea.value = true;

  const move = moveEvent => {
    width.value = Math.abs(moveEvent.clientX - startX);
    height.value = Math.abs(moveEvent.clientY - startY);
    if (moveEvent.clientX < startX) {
      start.value.x = moveEvent.clientX - editorX.value;
    }

    if (moveEvent.clientY < startY) {
      start.value.y = moveEvent.clientY - editorY.value;
    }
  };

  const up = e => {
    document.removeEventListener("mousemove", move);
    document.removeEventListener("mouseup", up);

    if (e.clientX == startX && e.clientY == startY) {
      hideArea();
      return;
    }

    createGroup();
  };

  document.addEventListener("mousemove", move);
  document.addEventListener("mouseup", up);
}

function hideArea() {
  isShowArea.value = false;
  width.value = 0;
  height.value = 0;
  setAreaData({
    style: {
      left: 0,
      top: 0,
      width: 0,
      height: 0
    },
    components: []
  });
}
function createGroup() {
  // 获取选中区域的组件数据
  const areaData = getSelectArea();
  if (areaData.length <= 1) {
    hideArea();
    return;
  }

  // 根据选中区域和区域中每个组件的位移信息来创建 Group 组件
  // 要遍历选择区域的每个组件，获取它们的 left top right bottom 信息来进行比较
  let top = Infinity,
    left = Infinity;
  let right = -Infinity,
    bottom = -Infinity;
  areaData.forEach(component => {
    let style = {
      left: 0,
      top: 0,
      right: 0,
      bottom: 0
    };
    if (component.component == "Group") {
      component.propValue.forEach(item => {
        const rectInfo = $(`#component${item.id}`).getBoundingClientRect();
        style.left = rectInfo.left - editorX.value;
        style.top = rectInfo.top - editorY.value;
        style.right = rectInfo.right - editorX.value;
        style.bottom = rectInfo.bottom - editorY.value;

        if (style.left < left) left = style.left;
        if (style.top < top) top = style.top;
        if (style.right > right) right = style.right;
        if (style.bottom > bottom) bottom = style.bottom;
      });
    } else {
      style = getComponentRotatedStyle(component.style);
    }

    if (style.left < left) left = style.left;
    if (style.top < top) top = style.top;
    if (style.right > right) right = style.right;
    if (style.bottom > bottom) bottom = style.bottom;
  });

  start.value.x = left;
  start.value.y = top;
  width.value = right - left;
  height.value = bottom - top;

  // 设置选中区域位移大小信息和区域内的组件数据
  setAreaData({
    style: {
      left,
      top,
      width: width.value,
      height: height.value
    },
    components: areaData
  });
}
function getSelectArea() {
  const result = [];
  // 区域起点坐标
  const { x, y } = start.value;
  const selectRight = x + width.value;
  const selectBottom = y + height.value;

  // 计算所有的组件数据，判断是否在选中区域内
  componentData.value.forEach(component => {
    // if (component.isLock) return;
    if (component.style) {
      const { display, visibility } = component.style;
      if (display === "none" || visibility === "hidden") {
        return;
      }
    }
    const { left, top, width: compWidth, height: compHeight } = getComponentRotatedStyle(component.style);

    const compRight = left + compWidth;
    const compBottom = top + compHeight;

    // Check if there’s any overlap
    if (left < selectRight && compRight > x && top < selectBottom && compBottom > y) {
      result.push(component);
    }
  });

  // 返回在选中区域内的所有组件
  return result;
}

// 接收组件触发的propValue变更
function editorEvent({ oKey = "propValue", cKey, cVal, id }) {
  console.log(cKey, cVal);
  for (let item of componentData.value) {
    if (item.id === id) {
      if (item[oKey] && item[oKey][cKey]) {
        item[oKey][cKey] = cVal;
      } else if (!cKey) {
        if (typeof cVal === "object") {
          Object.assign(item[oKey], cVal);
        } else {
          item[oKey] = cVal;
        }
      }
      return true;
    }
  }
}
onMounted(() => {
  const { getEditor } = useCompose();
  const { editor } = storeToRefs(useCompose());
  getEditor();
  $editor.value = editor.value;
  // document.addEventListener("keydown", ctrlDown, true);
  // document.addEventListener("keyup", ctrlUp, true);
  eventBus.on("hideArea", () => {
    hideArea();
  });
  eventBus.on("isShowArea", () => {
    isShowArea.value = true;
  });
});
onUnmounted(() => {
  document.removeEventListener("keydown", ctrlDown);
  document.removeEventListener("keydown", ctrlUp);
});
const ctrlKey = 17,
  commandKey = 91;
function ctrlDown(e) {
  const { keyCode } = e;
  if (keyCode === ctrlKey || keyCode === commandKey) {
    isCtrlOrCommandDown = true;
  }
}
function ctrlUp(e) {
  if (e.keyCode === ctrlKey || e.keyCode === commandKey) {
    isCtrlOrCommandDown = false;
  }
}
</script>

<style lang="scss">
.editor {
  position: relative;
  height: 100%;
  background: #fff;

  .component {
    width: 100%;
    height: 100%;
    outline: none;
  }

  .lock {
    opacity: 0.5;

    &:hover {
      cursor: not-allowed;
    }
  }
}
</style>
